import { createElements, define, itag, Styled } from "./itag/index.js";
import { ApiList } from "./ApiList.js";
import { LoggerElement } from "./LoggerElement/LogElement.js";
import { RequestDetailElement } from "./req/RequestDetailElement.js";
import type { RequestOption } from "./req/RequestOption.js";
import { Parser } from "./res/Parser.js";
import { ResponseDetailElement } from "./res/ResponseDetailElement.js";
import type { ResponseOption } from "./res/ResponseOption.js";

const [styled, css] = Styled.new();

css`
    :host {
        height: 100%;
    }
`;

const layout = styled.class("layout")`
    box-sizing: border-box;
    display: flex;
    flex-direction: row;
    height: 100%;
    overflow: hidden;
    width: 100%;
`;

const left = styled.class("left")`
    width: 20em;
    flex-shrink: 0;

    h3 {
        padding: 0.6em 1em;
        margin: 0;
        font-weight: inherit;
    }
`;

const splitter = styled.class("splitter")`
    width: 1px;
    border-left: 1px solid #ccc;
`;

const right = styled.class("right")`
    box-sizing: border-box;
    display: flex;
    flex-grow: 1;
    flex-direction: column;
    gap: 1em;
    height: 100%;
    overflow: hidden;
`;

const detail = styled.class("detail")`
    box-sizing: border-box;
`;

const logger = styled.class("logger")`
    flex-grow: 1;
    flex-shrink: 1;
    overflow-y: auto;
    overflow-x: hidden;
`;

function storageFocus(kind: "request" | "response", name: string) {
    localStorage.setItem("focus", JSON.stringify({ kind, name }));
}

function getStorageFocus(): { kind: "request" | "response"; name: string } | null {
    const data = localStorage.getItem("focus");
    if (data === null) {
        return null;
    }
    try {
        const { kind, name } = JSON.parse(data);
        if (kind !== "request" && kind !== "response") {
            return null;
        }

        if (typeof name !== "string") {
            return null;
        }
        return { kind, name };
    } catch (e) {
        return null;
    }
}

export interface BinaryViewElement {
    addEventListener(
        type: "submit",
        listener: (this: this, ev: CustomEvent<{ api: string; data: number[] }>) => void,
        options?: boolean | AddEventListenerOptions
    ): void;

    addEventListener(
        type: string,
        listener: EventListenerOrEventListenerObject,
        options?: boolean | AddEventListenerOptions
    ): void;
}

export class BinaryViewElement extends HTMLDivElement {
    readonly #elms = createElements({
        reqList: ApiList,
        resList: ApiList,
        detail: "div",
        reqDetail: RequestDetailElement,
        resDetail: ResponseDetailElement,
        logger: LoggerElement,
        clear: "button",
    });

    readonly #requests: Map<string, RequestOption> = new Map();

    readonly #responses: Map<string, ResponseOption> = new Map();

    constructor() {
        super();
        this.#requests = new Map();
        const sr = this.attachShadow({ mode: "closed" });
        sr.adoptedStyleSheets.push(styled.sheet);
        itag`
${sr}
    div .${layout}
        div .${left}
            h3 : 请求列表
            ${this.#elms.reqList}
            h3 : 响应列表
            ${this.#elms.resList}
        div .${splitter}
        div .${right}
            ${this.#elms.detail} .${detail}
            div .${logger}
                ${this.#elms.logger}
            div
                ${this.#elms.clear} : 清除
`;

        this.#elms.reqList.addEventListener("change", () => {
            this.#elms.resList.value = undefined;
            this.#setRequest(this.#elms.reqList.value);
        });

        this.#elms.reqDetail.addEventListener("submit", (ev) => {
            this.dispatchEvent(
                new CustomEvent("submit", {
                    detail: ev.detail,
                })
            );
        });

        this.#elms.resList.addEventListener("change", () => {
            this.#elms.reqList.value = undefined;
            this.#setResponse(this.#elms.resList.value);
        });

        this.#elms.clear.addEventListener("click", () => {
            this.#elms.logger.clear();
        });
    }

    setRequest(requests: Record<string, RequestOption>) {
        for (const name in requests) {
            const option = requests[name]!;
            this.#elms.reqList.add(name, option.name);
            this.#requests.set(name, option);
        }
        const focus = getStorageFocus();
        if (focus?.kind === "request") {
            this.#elms.reqList.value = focus.name;
            this.#setRequest(focus.name);
        }
    }

    setResponse(responses: Record<string, ResponseOption>) {
        for (const name in responses) {
            const option = responses[name]!;
            this.#elms.resList.add(name, option.name);
            this.#responses.set(name, option);
        }
        const focus = getStorageFocus();
        if (focus?.kind === "response") {
            this.#elms.resList.value = focus.name;
            this.#setResponse(focus.name);
        }
    }

    log(message: string): void {
        this.#elms.logger.log(message);
    }

    parse(data: Uint8Array) {
        let pass: ResponseOption | null = null;

        for (const res of this.#responses.values()) {
            const checker = new Parser(res);
            const result = checker.check(data);
            if (result === null) {
                continue;
            }
            if (pass !== null) {
                this.#elms.logger.log("无法解析数据响应");
            } else {
                pass = res;
                this.#elms.logger.log("数据解析成功\n" + result);
            }
        }
    }

    #setRequest(name: string | undefined) {
        this.#elms.reqDetail.remove();
        this.#elms.resDetail.remove();
        if (name === undefined) {
            return;
        }

        const view = this.#elms.reqDetail;
        view.reset();
        if (name === undefined) {
            return;
        }
        const option = this.#requests.get(name);
        if (option === undefined) {
            return;
        }
        view.api = name;
        view.description = option.description;
        for (const data of option.fields) {
            view.add(data);
        }
        this.#elms.detail.appendChild(view);
        storageFocus("request", name);
    }

    #setResponse(name: string | undefined) {
        this.#elms.reqDetail.remove();
        this.#elms.resDetail.remove();
        if (name === undefined) {
            return;
        }
        const option = this.#responses.get(name);
        if (option === undefined) {
            return;
        }
        const view = this.#elms.resDetail;
        view.show(option);
        this.#elms.detail.appendChild(view);
        storageFocus("response", name);
    }
}

define("binary-view", BinaryViewElement, {
    extends: "div",
});
